package com.edooschool.oral.arithmetic.web.interceptor;

import java.io.IOException;
import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.demo2do.core.utils.StringUtils;
import com.demo2do.core.web.utils.WebUtils;

/**
 * 
 * @author mohanlan
 *
 */
public class UserContextInterceptor extends HandlerInterceptorAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class);
    
    @Value("#{config['tencent.host']}")
    private String tencentHost;
    
    private static final String USER_AGENT = "User-Agent";
    
    private static final String MICRO_MESSENGER = "MicroMessenger";
    
    private static final String OPENID_KEY = "edooschool-wechat-openid";
    
    public static final String USER_ACCOUNT_KEY = "edooschool-user-account";
    
    public static final String MOBILE_KEY = "edooschool-mobile";
    
    /**
     * URL to display user register url
     */
    private String registerUrl;
    
    /**
     * URL to display general page describes "require WeChat browser environment"
     */
    private String wechatRequiredUrl;
    
    /**
     * URL for wechat auth
     */
    private String wechatAuthUrl;
    
    /**
     * 
     * @param wechatRequiredUrl the wechatRequiredUrl to set
     */
    public void setWechatRequiredUrl(String wechatRequiredUrl) {
        this.wechatRequiredUrl = wechatRequiredUrl;
    }
    
    /**
     * 
     * @param registerUrl the registerUrl to set
     */
    public void setRegisterUrl(String registerUrl) {
        this.registerUrl = registerUrl;
    }
    
    /**
     * 
     * @param wechatAuthUrl the wechatAuthUrl to set
     */
    public void setWechatAuthUrl(String wechatAuthUrl) {
        this.wechatAuthUrl = wechatAuthUrl;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        
        if (handler instanceof HandlerMethod) {
            
            if(logger.isDebugEnabled()) {
                logger.debug("MobileSniffingInterceptor#preHandle() - To determine this request can be visited in addition to wechat client");
            }
            
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            
            UserContext userContext = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), UserContext.class);
            
            // NONE: do nothing, return super
            if(userContext == null || userContext.type().equals(UserContextType.NONE)) {
                return super.preHandle(request, response, handler);
            }
            
            logger.trace("#preHandle - start user constent interceptor");
            
            String originUrl = this.generateOriginUrl(request, response);
            logger.trace("#preHandle - origin url [{}]", originUrl);
            
            String redirectUrl = null;
            
            // REGISTER: validate mobile cookie and 
            //           recirect to register page if cookie not exit
            if(userContext.type().equals(UserContextType.REGISTER)) {
                String registerUrl = this.generateRegisterUrl(originUrl, request, response);
                if(org.apache.commons.lang3.StringUtils.isNoneBlank(registerUrl)) {
                    logger.trace("#preHandle - redirect to register url[{}]", registerUrl);
                    redirectUrl = registerUrl;
                }
                
            // WECHAT_REQUIRED: check enviroment and redirect to wechat 
            //                  required page if env is microMessenger
            } else if(userContext.type().equals(UserContextType.WECHAT_REQUIRED)) {
                if(this.isMicroMessenger(request)) {
                    String wechatRequiredUrl = this.wechatRequiredUrl.startsWith("http") ? 
                                            StringUtils.connect(WebUtils.getRealServerPath(request), this.wechatRequiredUrl) :
                                            this.wechatRequiredUrl;
                    redirectUrl = wechatRequiredUrl;
                }
            
            // WECHAT_BASE: check env & cookie,
            //              if env is microMessenger & openid cookie not exit
            //              redirect to wechat base auth url
            } else if(userContext.type().equals(UserContextType.WECHAT_BASE)) {
                if(this.needWechatBaseAuth(request)) {
                    redirectUrl = this.generateAuthPrepare(originUrl, "snsapi_base", request);
                }
                
            // WECHAT_AUTHORITY: check env & cookie,
            //                   if env is microMessenger & openid cookie not exit 
            //                   & user account cookie not exit
            //                   redirect to wechat authority url
            } else if(userContext.type().equals(UserContextType.WECHAT_AUTHORITY)) {
                if(this.needWechatAuthority(request)) {
                    redirectUrl = this.generateAuthPrepare(originUrl, "snsapi_userinfo", request);
                }
            }
            
            // FINAL: if redirect url exit, do redirect here
            if(org.apache.commons.lang3.StringUtils.isNoneEmpty(redirectUrl)) {
                response.sendRedirect(redirectUrl);
                return false;
            }
        }
        
        return super.preHandle(request, response, handler);
    }
    
    /**
     * 
     * @param request
     * @param response
     * @return
     */
    private String generateOriginUrl(HttpServletRequest request, HttpServletResponse response) {
        
        String client = WebUtils.getRealServerPath(request);
        
        String originalUri = WebUtils.getRealRequestURI(request);
        String queryString = request.getQueryString();
        
        String originalUrl = StringUtils.connect(client, 
                             org.apache.commons.lang3.StringUtils.isBlank(queryString) ? 
                             originalUri : 
                             StringUtils.connect("?", queryString));
        
        return originalUrl;
    }
    
    /**
     * 
     * @param originUrl
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    private String generateRegisterUrl(String originUrl, HttpServletRequest request, HttpServletResponse response) throws IOException {
        
        Cookie mobileCookie = org.springframework.web.util.WebUtils.getCookie(request, MOBILE_KEY);
        
        if(mobileCookie != null) {
            return null;
        }
        
        String registerUrl = this.registerUrl.startsWith("http") ? 
                StringUtils.connect(WebUtils.getRealServerPath(request), this.registerUrl) :
                this.registerUrl;

        String redirect = StringUtils.connect(registerUrl, "?redirectUrl", originUrl);
        return redirect;
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean isMicroMessenger(HttpServletRequest request) {
        
        String userAgent = request.getHeader(USER_AGENT);
        logger.info("#isMicroMessenger() - Get user agent [{}].", userAgent);
        
        return org.apache.commons.lang3.StringUtils.isNoneBlank(userAgent) && userAgent.indexOf(MICRO_MESSENGER) >= 0;
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean needWechatBaseAuth(HttpServletRequest request) {
        
        // validate env
        if(!this.isMicroMessenger(request)) {
            return false;
        }
        
        // validate openid cookie
        Cookie openidCookie = org.springframework.web.util.WebUtils.getCookie(request, OPENID_KEY);
        if(openidCookie != null) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 
     * @param request
     * @return
     */
    private boolean needWechatAuthority(HttpServletRequest request) {
        
        // validate env
        if(!this.isMicroMessenger(request)) {
            return false;
        }
        
        // validate openid cookie
        Cookie openidCookie = org.springframework.web.util.WebUtils.getCookie(request, OPENID_KEY);
        if(openidCookie != null) {
            return false;
        }
        
        // valdiate account cookie
        Cookie accountCookie = org.springframework.web.util.WebUtils.getCookie(request, USER_ACCOUNT_KEY);
        if(accountCookie != null) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 
     * @param originUrl
     * @param type
     * @return
     */
    private String generateAuthPrepare(String originUrl, String type, HttpServletRequest request) {
        
        String authPrepare = this.wechatAuthUrl.startsWith("http") ? 
                               wechatAuthUrl : 
                               StringUtils.connect(tencentHost + wechatAuthUrl);
        
        String client = com.demo2do.core.web.utils.WebUtils.getRealServerPath(request);
        
        String state = String.valueOf(new Date().getTime());
        
        return StringUtils.connect(authPrepare, 
                                   "?scope=", type, 
                                   "&redirectUrl=", originUrl,
                                   "&client=", client,
                                   "&requestNo=", state);
    }
}
